package org.stimuliband.dsp.goertzel.dtmf;

import java.text.NumberFormat;
import java.util.Locale;
import org.stimuliband.dsp.goertzel.CachedCoefficients;
import org.stimuliband.dsp.goertzel.Coefficients;
import org.stimuliband.dsp.goertzel.Goertzel;
import org.stimuliband.dsp.goertzel.HammingWindow;

public class TryDetect implements MicrophoneListener {

    final Coefficients coeff;
    final Goertzel goertzel;
    final NumberFormat formatter;
    final HammingWindow window;
    public final static String[][] events = new String[][]{
        {"1", "2", "3", "A"},
        {"4", "5", "6", "B"},
        {"7", "8", "9", "C"},
        {"*", "0", "#", "D"}
    };

    public TryDetect(int samplingRate, int probesGroupSize) {
        this.coeff = new CachedCoefficients(samplingRate, probesGroupSize);
        this.goertzel = new Goertzel(coeff);

        // todo: can be different window function
        this.window = new HammingWindow(probesGroupSize);

        formatter = NumberFormat.getNumberInstance(Locale.ENGLISH);
        formatter.setMaximumFractionDigits(0);
        formatter.setMinimumFractionDigits(0);
        formatter.setGroupingUsed(false);

        header();
    }
    // filtering parameters
    int AMPLITUDE_THRESHOLD = 10;
    double RELATION_IN_GROUP_THRESHOLD = 18;

    @Override
    public void onCaptured(byte[] captured) {
        double[] signal = new double[captured.length];
        int maxAmplitude = 0;

        for (int i = 0; i < captured.length; i++) {
            // casting into double
            signal[i] = captured[i];

            // determine max amplitude
            int ampl = Math.abs(((int) captured[i]));
            if (ampl > maxAmplitude) {
                maxAmplitude = ampl;
            }
        }

        double[] signalWindowed = window.filter(signal);

        double[] low = new double[DtmfFrequences.LOW.length];
        for (int i = 0; i < DtmfFrequences.LOW.length; i++) {
            low[i] = goertzel.magnitude(signalWindowed, DtmfFrequences.LOW[i]);
        }
        double[] high = new double[DtmfFrequences.HIGH.length];
        for (int i = 0; i < DtmfFrequences.HIGH.length; i++) {
            high[i] = goertzel.magnitude(signalWindowed, DtmfFrequences.HIGH[i]);
        }

        int[] maxLow = getMax(low);
        int[] maxHigh = getMax(high);

        boolean allow = true;

        if (allow && (maxAmplitude < AMPLITUDE_THRESHOLD)) {
            allow = false;
        }

        if (allow) {
            allow = allow(low[maxLow[0]], maxLow[0], low, RELATION_IN_GROUP_THRESHOLD);
        }
        if (allow) {
            allow = allow(high[maxHigh[0]], maxHigh[0], high, RELATION_IN_GROUP_THRESHOLD);
        }

        if (allow) {
            StringBuilder sb = new StringBuilder();

            sb.append(events[maxLow[0]][maxHigh[0]]);
            sb.append("\t");

            sb.append(prt(maxAmplitude));
            sb.append("\t");

            sb.append(prt(low[0]));
            sb.append("\t");
            sb.append(prt(low[1]));
            sb.append("\t");
            sb.append(prt(low[2]));
            sb.append("\t");
            sb.append(prt(low[3]));
            sb.append("\t");

            sb.append(prt(high[0]));
            sb.append("\t");
            sb.append(prt(high[1]));
            sb.append("\t");
            sb.append(prt(high[2]));
            sb.append("\t");
            sb.append(prt(high[3]));

            System.out.println(sb.toString());
        }
    }

    void header() {
        StringBuilder sb = new StringBuilder();

        sb.append("DTMF");
        sb.append("\t");
        sb.append("MaxAmpl");
        sb.append("\t");

        sb.append("L0");
        sb.append("\t");
        sb.append("L1");
        sb.append("\t");
        sb.append("L2");
        sb.append("\t");
        sb.append("L3");
        sb.append("\t");

        sb.append("H0");
        sb.append("\t");
        sb.append("H1");
        sb.append("\t");
        sb.append("H2");
        sb.append("\t");
        sb.append("H3");

        System.out.println(sb.toString());
    }

    String prt(double value) {
        return formatter.format(value);
    }

    boolean allow(double max, int ignoredIndex, double[] powers, double threshold) {
        for (int i = 0; i < powers.length; i++) {
            if (ignoredIndex == i) {
                continue;
            }
            double r = max / (powers[i] + 1E-15);

            if (r < threshold) {
                return false;
            }
        }
        return true;
    }

    private int[] getMax(double data[]) {
        int idx[] = new int[2];

        // looking for first max index
        double max = Double.MIN_VALUE;
        for (int i = 0; i < data.length; i++) {
            if (max < data[i]) {
                max = data[i];
                idx[0] = i;
            }
        }

        // looking for second max index
        max = Double.MIN_VALUE;
        for (int i = 0; i < data.length; i++) {
            if (i != idx[0] && max < data[i]) {
                max = data[i];
                idx[1] = i;
            }
        }

        return idx;
    }
}
